package structures;

import java.util.ArrayList;

import util.SummedGaussian;
import util.Util;
import main.Main;
import main.Initializable;
import main.InitializationException;

public class Column implements Initializable
{
	private Mitral mitralCell;
	private ArrayList<Granule> granules;
	
	private double output = -1;
	
	private SummedGaussian granuleActivationThresholdGaussian;
	private double granuleToMitralWeight = 1.00;
	
	private static double DEFAULT_GRANULE_GAUSSIAN_STDEV = 25.00;
	
	public Column(Network n)
	{
		granules = new ArrayList<Granule>(n.getGranulesPerColumn());
		mitralCell = new Mitral(n.getMitralToGranuleWeights());
		granuleToMitralWeight = n.getGranuleToMitralWeight().getRandomValue();
	}
	
	public ArrayList<Granule> getGranules()
	{
		return this.granules;
	}
	
	public double getAverageGranuleActivationThreshold()
	{
		double avg = 0, sum = 0;
		
		for (Granule g : this.granules)
		{
			sum += g.getActivationThreshold();
		}
		
		avg = (sum)/(this.granules.size()+0.00);
		
		return avg;
	}
	
	public Mitral getMitral()
	{
		return this.mitralCell;
	}
	
	private boolean verifyGranuleInitialization()
	{
		if (this.granules == null)
			return false;
		else
		{
			for (Granule g : granules)
			{
				if (!g.isInitialized())
				{
					return false;
				}
			}
			
			return true;
		}
	} 	
	
	public boolean isInitialized()
	{
		return this.mitralCell.isInitialized() && this.verifyGranuleInitialization();
	}

	public void initialize(Object obj) throws InitializationException
	{
		Network network;
		
		if (obj instanceof Network)
			network = (Network) obj;
		else
			throw new InitializationException("Paramater not of type Network: Aborting Column initialization");
		
		
		//Initialize mitral cells
		this.mitralCell.setParentIndex(network.indexOf(this));
		this.mitralCell.initialize(network);
		
		this.granuleActivationThresholdGaussian = 
				new SummedGaussian(DEFAULT_GRANULE_GAUSSIAN_STDEV, Main.ODOR_INPUT_SUM*3.0);
		
		//Initialize granules
		//TODO Mess around w/ weights here
		
		double[] activationThresholdArray = this.granuleActivationThresholdGaussian.getNumRandomZScores(network.getGranulesPerColumn())[1];
		
		util.Util.arrayMean(activationThresholdArray);
		util.Util.standardDeviation(activationThresholdArray);
		
		for(int x=0; x < network.getGranulesPerColumn(); x++)
		{
			double toMitralWeight = 0, activationThreshold = 0;
			
			//STEP A: Set Granule-To-Mitral Weight
			toMitralWeight = this.granuleToMitralWeight;
			
			//STEP B: Set/Get Granular Threshold			
			activationThreshold = activationThresholdArray[x];
			
			granules.add(new Granule(activationThreshold, toMitralWeight, this, network, x+1));
		}
	}
}
